package cn.iocoder.yudao.module.system.service.userassetscode;

import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.module.system.controller.admin.userassetscode.vo.UserAssetsCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.userassetscode.vo.UserAssetsCodeSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.userassetscode.vo.UserAssetsCodeDetailRespVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.UserAssetsDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userassetschange.UserAssetsChangeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userassetscode.UserAssetsCodeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userassetscode.UserAssetsCodeUsageLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userretention.UserRetentionStatsDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.UserAssetsMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userassetschange.UserAssetsChangeMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userassetscode.UserAssetsCodeMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userassetscode.UserAssetsCodeUsageLogMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userretention.UserRetentionStatsMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 用户资源兑换 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class UserAssetsCodeServiceImpl implements UserAssetsCodeService {

    private static final Logger log = LoggerFactory.getLogger(UserAssetsCodeServiceImpl.class);

    @Resource
    private UserAssetsCodeMapper userAssetsCodeMapper;

    @Resource
    private UserAssetsMapper userAssetsMapper;

    @Resource
    AdminUserMapper adminUserMapper;

    @Resource
    UserAssetsChangeMapper assetsChangeMapper;

    @Resource
    private UserAssetsCodeUsageLogMapper usageLogMapper;

    @Resource
    private UserRetentionStatsMapper retentionStatsMapper;

    @Override
    public Integer createUserAssetsCode(UserAssetsCodeSaveReqVO createReqVO) {
        // 获取需要创建的兑换码数量
        Integer codeNum = createReqVO.getCodeNum();
        if (codeNum == null || codeNum <= 0) {
            codeNum = 1; // 默认创建一个兑换码
        }
        
        // 批量创建兑换码
        List<UserAssetsCodeDO> userAssetsCodes = new ArrayList<>();
        for (int i = 0; i < codeNum; i++) {
            // 创建基本对象并复制属性
            UserAssetsCodeDO userAssetsCode = BeanUtils.toBean(createReqVO, UserAssetsCodeDO.class);
            // 生成8位唯一兑换码
            userAssetsCode.setCode(generateUniqueCode());
            userAssetsCodes.add(userAssetsCode);
        }
        
        // 批量插入
        userAssetsCodes.forEach(userAssetsCodeMapper::insert);
        
        // 返回创建的数量
        return codeNum;
    }

    /**
     * 生成8位唯一兑换码，包含数字和大写字母
     *
     * @return 8位唯一兑换码
     */
    private String generateUniqueCode() {
        // 定义字符集：数字和大写字母
        String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        int length = 8;

        // 创建随机数生成器
        Random random = new Random();
        StringBuilder codeBuilder = new StringBuilder(length);

        // 生成8位随机字符串
        for (int i = 0; i < length; i++) {
            codeBuilder.append(characters.charAt(random.nextInt(characters.length())));
        }

        String code = codeBuilder.toString();

        // 检查数据库中是否已存在此兑换码
        LambdaQueryWrapperX<UserAssetsCodeDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(UserAssetsCodeDO::getCode, code);
        if (userAssetsCodeMapper.selectCount(queryWrapper) > 0) {
            // 如果已存在，则递归重新生成
            return generateUniqueCode();
        }

        return code;
    }

    @Override
    public void updateUserAssetsCode(UserAssetsCodeSaveReqVO updateReqVO) {
        // 校验存在
        validateUserAssetsCodeExists(updateReqVO.getId());
        // 更新
        UserAssetsCodeDO updateObj = BeanUtils.toBean(updateReqVO, UserAssetsCodeDO.class);
        userAssetsCodeMapper.updateById(updateObj);
    }

    @Override
    public void deleteUserAssetsCode(Integer id) {
        // 校验存在
        validateUserAssetsCodeExists(id);
        // 删除
        userAssetsCodeMapper.deleteById(id);
    }

    private void validateUserAssetsCodeExists(Integer id) {
        if (userAssetsCodeMapper.selectById(id) == null) {
            throw exception(new ErrorCode(404,"用户资源不存在"));
        }
    }

    @Override
    public UserAssetsCodeDO getUserAssetsCode(Integer id) {
        return userAssetsCodeMapper.selectById(id);
    }

    @Override
    public UserAssetsCodeDetailRespVO getUserAssetsCodeDetail(Long id) {
        // 获取兑换码基本信息
        UserAssetsCodeDO userAssetsCode = userAssetsCodeMapper.selectById(id.intValue());
        if (userAssetsCode == null) {
            throw exception(ErrorCodeConstants.USER_ASSETS_CODE_NOT_EXISTS);
        }

        // 构建详细响应VO
        UserAssetsCodeDetailRespVO detailVO = BeanUtils.toBean(userAssetsCode, UserAssetsCodeDetailRespVO.class);

        // 获取使用记录
        List<UserAssetsCodeUsageLogDO> usageLogs = usageLogMapper.selectByCodeId(id.intValue());
        
        // 构建使用者信息列表
        List<UserAssetsCodeDetailRespVO.UserInfo> userInfoList = new ArrayList<>();
        for (UserAssetsCodeUsageLogDO log : usageLogs) {
            UserAssetsCodeDetailRespVO.UserInfo userInfo = new UserAssetsCodeDetailRespVO.UserInfo();
            userInfo.setUserId(log.getUserId());
            userInfo.setUsername(log.getUsername());
            userInfo.setUsageTime(log.getUsageTime());
            userInfo.setUserIp(log.getUserIp());
            userInfo.setUsageResult(log.getUsageResult() != null ? (log.getUsageResult() == 1 ? "SUCCESS" : "FAILED") : "UNKNOWN");
            userInfo.setResourceInfo(log.getAssetsReceived());

            // 获取用户详细信息
            AdminUserDO adminUser = adminUserMapper.selectById(log.getUserId());
            if (adminUser != null) {
                userInfo.setNickname(adminUser.getNickname());
                userInfo.setMobile(adminUser.getMobile());
                userInfo.setEmail(adminUser.getEmail());
            }

            // 获取留存率数据
            UserRetentionStatsDO retentionStats = retentionStatsMapper.selectOneByUserIdAndDate(log.getUserId(), log.getUsageTime().toLocalDate());
            if (retentionStats != null) {
                userInfo.setRetention3Days(retentionStats.getRetention3Days());
                userInfo.setRetention7Days(retentionStats.getRetention7Days());
                userInfo.setRetention15Days(retentionStats.getRetention15Days());
                userInfo.setTotalLoginDays(retentionStats.getTotalLoginDays());
                userInfo.setConsecutiveLoginDays(retentionStats.getConsecutiveLoginDays());
            }

            userInfoList.add(userInfo);
        }
        detailVO.setUserInfoList(userInfoList);

        // 计算留存率统计
        UserAssetsCodeDetailRespVO.RetentionStats retentionStats = calculateRetentionStats(usageLogs);
        detailVO.setRetentionStats(retentionStats);

        // 计算使用统计
        UserAssetsCodeDetailRespVO.UsageStats usageStats = calculateUsageStats(usageLogs);
        detailVO.setUsageStats(usageStats);

        return detailVO;
    }

    /**
     * 计算留存率统计
     */
    private UserAssetsCodeDetailRespVO.RetentionStats calculateRetentionStats(List<UserAssetsCodeUsageLogDO> usageLogs) {
        UserAssetsCodeDetailRespVO.RetentionStats stats = new UserAssetsCodeDetailRespVO.RetentionStats();
        
        if (usageLogs.isEmpty()) {
            return stats;
        }

        long totalUsers = usageLogs.size();
        stats.setTotalUsers(totalUsers);

        long retention3DayUsers = 0;
        long retention7DayUsers = 0;
        long retention15DayUsers = 0;
        long activeUsers = 0;
        int totalLoginDays = 0;

        for (UserAssetsCodeUsageLogDO log : usageLogs) {
            UserRetentionStatsDO retentionData = retentionStatsMapper.selectOneByUserIdAndDate(log.getUserId(), log.getUsageTime().toLocalDate());
            if (retentionData != null) {
                if (Boolean.TRUE.equals(retentionData.getRetention3Days())) {
                    retention3DayUsers++;
                }
                if (Boolean.TRUE.equals(retentionData.getRetention7Days())) {
                    retention7DayUsers++;
                }
                if (Boolean.TRUE.equals(retentionData.getRetention15Days())) {
                    retention15DayUsers++;
                }
                if (retentionData.getTotalLoginDays() != null && retentionData.getTotalLoginDays() > 0) {
                    activeUsers++;
                    totalLoginDays += retentionData.getTotalLoginDays();
                }
            }
        }

        stats.setRetention3DayUsers(retention3DayUsers);
        stats.setRetention7DayUsers(retention7DayUsers);
        stats.setRetention15DayUsers(retention15DayUsers);
        stats.setActiveUsers(activeUsers);
        stats.setChurnUsers(totalUsers - activeUsers);

        // 计算留存率
        stats.setRetention3DayRate(totalUsers > 0 ? (retention3DayUsers * 100.0 / totalUsers) : 0.0);
        stats.setRetention7DayRate(totalUsers > 0 ? (retention7DayUsers * 100.0 / totalUsers) : 0.0);
        stats.setRetention15DayRate(totalUsers > 0 ? (retention15DayUsers * 100.0 / totalUsers) : 0.0);
        stats.setAverageLoginDays(activeUsers > 0 ? (totalLoginDays * 1.0 / activeUsers) : 0.0);

        return stats;
    }

    /**
     * 计算使用统计
     */
    private UserAssetsCodeDetailRespVO.UsageStats calculateUsageStats(List<UserAssetsCodeUsageLogDO> usageLogs) {
        UserAssetsCodeDetailRespVO.UsageStats stats = new UserAssetsCodeDetailRespVO.UsageStats();
        
        if (usageLogs.isEmpty()) {
            return stats;
        }

        long totalUsageCount = usageLogs.size();
        long successUsageCount = usageLogs.stream()
                .mapToLong(log -> Integer.valueOf(1).equals(log.getUsageResult()) ? 1 : 0)
                .sum();
        long failedUsageCount = totalUsageCount - successUsageCount;

        stats.setTotalUsageCount(totalUsageCount);
        stats.setSuccessUsageCount(successUsageCount);
        stats.setFailedUsageCount(failedUsageCount);
        stats.setSuccessRate(totalUsageCount > 0 ? (successUsageCount * 100.0 / totalUsageCount) : 0.0);

        // 获取首次和最近使用时间
        LocalDateTime firstUsageTime = usageLogs.stream()
                .map(UserAssetsCodeUsageLogDO::getUsageTime)
                .min(LocalDateTime::compareTo)
                .orElse(null);
        LocalDateTime lastUsageTime = usageLogs.stream()
                .map(UserAssetsCodeUsageLogDO::getUsageTime)
                .max(LocalDateTime::compareTo)
                .orElse(null);

        stats.setFirstUsageTime(firstUsageTime);
        stats.setLastUsageTime(lastUsageTime);

        // 计算平均每日使用次数
        if (firstUsageTime != null && lastUsageTime != null) {
            long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(firstUsageTime.toLocalDate(), lastUsageTime.toLocalDate()) + 1;
            stats.setAverageDailyUsage(daysBetween > 0 ? (totalUsageCount * 1.0 / daysBetween) : (double) totalUsageCount);
        } else {
            stats.setAverageDailyUsage((double) totalUsageCount);
        }

        return stats;
    }

    @Override
    public PageResult<UserAssetsCodeDO> getUserAssetsCodePage(UserAssetsCodePageReqVO pageReqVO) {
        return userAssetsCodeMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserAssetsCodeDO redeemUserAssetsCode(String code, Long userId) {
        LocalDateTime usageTime = LocalDateTime.now();
        String traceId = generateTraceId();
        
        // 获取用户信息和请求信息
        String userIp = getCurrentUserIp();
        String userAgent = getCurrentUserAgent();
        String username = getCurrentUsername(userId);
        Integer userType = getCurrentUserType();
        
        log.info("开始兑换码使用流程，兑换码：{}，用户ID：{}，IP：{}，TraceId：{}", code, userId, userIp, traceId);
        
        // 创建使用记录对象（先创建，后续根据结果更新）
        UserAssetsCodeUsageLogDO usageLog = UserAssetsCodeUsageLogDO.builder()
                .code(code)
                .userId(userId)
                .username(username)
                .userType(userType)
                .userIp(userIp)
                .userAgent(userAgent)
                .deviceInfo(buildDeviceInfo(userAgent))
                .usageTime(usageTime)
                .usageResult(0) // 先设为失败，成功后再更新
                .traceId(traceId)
                .build();
        
        try {
            // 1. 查询兑换码是否存在
            LambdaQueryWrapperX<UserAssetsCodeDO> queryWrapper = new LambdaQueryWrapperX<>();
            queryWrapper.eq(UserAssetsCodeDO::getCode, code);
            UserAssetsCodeDO userAssetsCode = userAssetsCodeMapper.selectOne(queryWrapper);
            if (userAssetsCode == null) {
                usageLog.setFailureReason("兑换码不存在");
                throw exception(new ErrorCode(107_852_000,"兑换码不存在"));
            }
            
            // 设置兑换码相关信息
            usageLog.setCodeId(userAssetsCode.getId());
            usageLog.setChangeId(userAssetsCode.getChangeId());
            usageLog.setChangeName(userAssetsCode.getChangeName());
            usageLog.setCustomerId(userAssetsCode.getCustomerId());
            usageLog.setCustomerName(userAssetsCode.getCustomerName());
            
            // 2. 检查兑换码状态，只有未使用的兑换码才能兑换
            if (userAssetsCode.getStatus() != null && userAssetsCode.getStatus() != 0) { // 假设0表示未使用状态
                usageLog.setFailureReason("兑换码已被使用");
                throw exception(new ErrorCode(107_853_000,"兑换码已被使用"));
            }
            
            // 3. 检查兑换码是否过期
            if (userAssetsCode.getExpireDate() != null && userAssetsCode.getExpireDate().isBefore(LocalDateTime.now())) {
                usageLog.setFailureReason("兑换码已过期");
                throw exception(new ErrorCode(107_854_000,"兑换码已过期"));
            }
            
            // 4. 更新兑换码状态为已使用，并记录使用者信息
            userAssetsCode.setUserId(userId);
            userAssetsCode.setUsedByUserId(userId);
            userAssetsCode.setUsedByUsername(username);
            userAssetsCode.setUsedIp(userIp);
            userAssetsCode.setUsedUserAgent(userAgent);
            userAssetsCode.setUsedDeviceInfo(buildDeviceInfo(userAgent));
            userAssetsCode.setStatus(1); // 假设1表示已使用状态
            userAssetsCode.setUsedDate(usageTime);
            userAssetsCodeMapper.updateById(userAssetsCode);


            //兑换成功后 增加用户权益
            UserAssetsChangeDO userAssetsChangeDO = assetsChangeMapper.selectOne("id",userAssetsCode.getChangeId());

            AdminUserDO adminUserDO = adminUserMapper.selectOne("id",userId);
            if (adminUserDO != null) {
                adminUserDO.setProTotalTimes(adminUserDO.getProTotalTimes() + userAssetsChangeDO.getPsCount());
                adminUserDO.setProLeftTimes(adminUserDO.getProLeftTimes()+userAssetsChangeDO.getPsCount());
                adminUserDO.setTestTotalTimes(adminUserDO.getTestTotalTimes()+userAssetsChangeDO.getTrailCount());
                adminUserDO.setTestLeftTimes(adminUserDO.getTestLeftTimes()+userAssetsChangeDO.getTrailCount());
                adminUserMapper.updateById(adminUserDO);
            }

            UserAssetsDO userAssetsDO = userAssetsMapper.selectOne(UserAssetsDO::getUserId, userId);
            if (userAssetsDO != null) {
            userAssetsDO.setContentCount(userAssetsDO.getContentCount() + userAssetsChangeDO.getContentCount());
            userAssetsDO.setContentLeftCount(userAssetsDO.getContentLeftCount() + userAssetsChangeDO.getContentCount());
            userAssetsDO.setAskCount(userAssetsDO.getAskCount() + userAssetsChangeDO.getAskCount());
            userAssetsDO.setAskLeftCount(userAssetsDO.getAskLeftCount() + userAssetsChangeDO.getAskCount());
            if(userAssetsChangeDO.getContentDays() != null && userAssetsChangeDO.getContentDays() > 0) {
                if(userAssetsDO.getContentEndTime() != null) {
                    if(userAssetsDO.getContentEndTime().isBefore(LocalDateTime.now())){
                        userAssetsDO.setContentStartTime(LocalDateTime.now());
                        userAssetsDO.setContentEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getContentDays()));
                    }else {
                        userAssetsDO.setContentEndTime(userAssetsDO.getContentEndTime().plusDays(userAssetsChangeDO.getContentDays()));
                    }
                }
            }

            // 处理模拟志愿VIP兑换
            if(userAssetsChangeDO.getSimulationDays() != null && userAssetsChangeDO.getSimulationDays() > 0) {
                if(userAssetsDO.getSimulationEndTime() != null) {
                    if(userAssetsDO.getSimulationEndTime().isBefore(LocalDateTime.now())){
                        // 如果当前VIP已过期，从现在开始计算
                        userAssetsDO.setSimulationStartTime(LocalDateTime.now());
                        userAssetsDO.setSimulationEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getSimulationDays()));
                        // 同时设置内容会员
                        userAssetsDO.setContentStartTime(LocalDateTime.now());
                        userAssetsDO.setContentEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getSimulationDays()));
                    }else {
                        // 如果当前VIP还有效，在现有时间基础上延长
                        userAssetsDO.setSimulationEndTime(userAssetsDO.getSimulationEndTime().plusDays(userAssetsChangeDO.getSimulationDays()));
                        // 同时延长内容会员
                        if(userAssetsDO.getContentEndTime() != null && userAssetsDO.getContentEndTime().isAfter(LocalDateTime.now())) {
                            userAssetsDO.setContentEndTime(userAssetsDO.getContentEndTime().plusDays(userAssetsChangeDO.getSimulationDays()));
                        } else {
                            userAssetsDO.setContentStartTime(LocalDateTime.now());
                            userAssetsDO.setContentEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getSimulationDays()));
                        }
                    }
                } else {
                    // 如果之前没有VIP，从现在开始
                    userAssetsDO.setSimulationStartTime(LocalDateTime.now());
                    userAssetsDO.setSimulationEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getSimulationDays()));
                    // 同时设置内容会员
                    userAssetsDO.setContentStartTime(LocalDateTime.now());
                    userAssetsDO.setContentEndTime(LocalDateTime.now().plusDays(userAssetsChangeDO.getSimulationDays()));
                }
                // 增加内容查看次数
                userAssetsDO.setContentCount(userAssetsDO.getContentCount() + 9999);
                userAssetsDO.setContentLeftCount(userAssetsDO.getContentLeftCount() + 9999);
            }

                userAssetsMapper.updateById(userAssetsDO);
            }
            
            // 5. 记录获得的资源信息
            Map<String, Object> assetsReceived = buildAssetsReceivedInfo(userAssetsChangeDO, adminUserDO, userAssetsDO);
            usageLog.setAssetsReceived(JsonUtils.toJsonString(assetsReceived));
            userAssetsCode.setAssetsReceivedInfo(JsonUtils.toJsonString(assetsReceived));
            userAssetsCodeMapper.updateById(userAssetsCode);
            
            // 6. 更新使用记录为成功
            usageLog.setUsageResult(1);
            usageLogMapper.insert(usageLog);
            
            // 7. 创建留存率统计记录
            createRetentionStatsRecord(userId, username, userType, userAssetsCode.getId(), code, usageLog.getId(), usageTime.toLocalDate());
            
            log.info("兑换码使用成功，兑换码：{}，用户ID：{}，TraceId：{}", code, userId, traceId);
            
            // 8. 返回兑换的资源信息
            return userAssetsCode;
            
        } catch (Exception e) {
            // 记录失败原因
            usageLog.setFailureReason(e.getMessage());
            usageLogMapper.insert(usageLog);
            log.error("兑换码使用失败，兑换码：{}，用户ID：{}，TraceId：{}，错误：{}", code, userId, traceId, e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 生成链路追踪ID
     */
    private String generateTraceId() {
        return "TRACE_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 10000);
    }

    /**
     * 获取当前用户IP
     */
    private String getCurrentUserIp() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                return ServletUtils.getClientIP(request);
            }
        } catch (Exception e) {
            log.warn("获取用户IP失败", e);
        }
        return "unknown";
    }

    /**
     * 获取当前用户Agent
     */
    private String getCurrentUserAgent() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                return request.getHeader("User-Agent");
            }
        } catch (Exception e) {
            log.warn("获取用户Agent失败", e);
        }
        return "unknown";
    }

    /**
     * 获取当前用户名
     */
    private String getCurrentUsername(Long userId) {
        try {
            AdminUserDO adminUser = adminUserMapper.selectOne("id", userId);
            if (adminUser != null) {
                return adminUser.getUsername();
            }
        } catch (Exception e) {
            log.warn("获取用户名失败，用户ID：{}", userId, e);
        }
        return "user_" + userId;
    }

    /**
     * 获取当前用户类型
     */
    private Integer getCurrentUserType() {
        // 这里可以根据实际业务逻辑判断用户类型
        // 1-管理员用户，2-会员用户
        return 1; // 默认为管理员用户
    }

    /**
     * 构建设备信息
     */
    private String buildDeviceInfo(String userAgent) {
        Map<String, Object> deviceInfo = new HashMap<>();
        deviceInfo.put("userAgent", userAgent);
        deviceInfo.put("timestamp", System.currentTimeMillis());
        
        // 简单的设备类型判断
        if (userAgent != null) {
            String lowerAgent = userAgent.toLowerCase();
            if (lowerAgent.contains("mobile") || lowerAgent.contains("android") || lowerAgent.contains("iphone")) {
                deviceInfo.put("deviceType", "mobile");
            } else if (lowerAgent.contains("tablet") || lowerAgent.contains("ipad")) {
                deviceInfo.put("deviceType", "tablet");
            } else {
                deviceInfo.put("deviceType", "desktop");
            }
            
            // 浏览器类型判断
            if (lowerAgent.contains("chrome")) {
                deviceInfo.put("browser", "chrome");
            } else if (lowerAgent.contains("firefox")) {
                deviceInfo.put("browser", "firefox");
            } else if (lowerAgent.contains("safari")) {
                deviceInfo.put("browser", "safari");
            } else if (lowerAgent.contains("edge")) {
                deviceInfo.put("browser", "edge");
            } else {
                deviceInfo.put("browser", "other");
            }
        }
        
        return JsonUtils.toJsonString(deviceInfo);
    }

    /**
     * 构建获得的资源信息
     */
    private Map<String, Object> buildAssetsReceivedInfo(UserAssetsChangeDO userAssetsChangeDO, 
                                                       AdminUserDO adminUserDO, UserAssetsDO userAssetsDO) {
        Map<String, Object> assetsReceived = new HashMap<>();
        
        if (userAssetsChangeDO != null) {
            assetsReceived.put("changeName", userAssetsChangeDO.getChangeName());
            assetsReceived.put("psCount", userAssetsChangeDO.getPsCount());
            assetsReceived.put("trailCount", userAssetsChangeDO.getTrailCount());
            assetsReceived.put("contentCount", userAssetsChangeDO.getContentCount());
            assetsReceived.put("askCount", userAssetsChangeDO.getAskCount());
            assetsReceived.put("contentDays", userAssetsChangeDO.getContentDays());
            assetsReceived.put("simulationDays", userAssetsChangeDO.getSimulationDays());
        }
        
        // 记录用户当前的资源状态
        Map<String, Object> userCurrentAssets = new HashMap<>();
        if (adminUserDO != null) {
            userCurrentAssets.put("proTotalTimes", adminUserDO.getProTotalTimes());
            userCurrentAssets.put("proLeftTimes", adminUserDO.getProLeftTimes());
            userCurrentAssets.put("testTotalTimes", adminUserDO.getTestTotalTimes());
            userCurrentAssets.put("testLeftTimes", adminUserDO.getTestLeftTimes());
        }
        
        if (userAssetsDO != null) {
            userCurrentAssets.put("contentCount", userAssetsDO.getContentCount());
            userCurrentAssets.put("contentLeftCount", userAssetsDO.getContentLeftCount());
            userCurrentAssets.put("askCount", userAssetsDO.getAskCount());
            userCurrentAssets.put("askLeftCount", userAssetsDO.getAskLeftCount());
            userCurrentAssets.put("contentEndTime", userAssetsDO.getContentEndTime());
            userCurrentAssets.put("simulationEndTime", userAssetsDO.getSimulationEndTime());
        }
        
        assetsReceived.put("userCurrentAssets", userCurrentAssets);
        assetsReceived.put("timestamp", System.currentTimeMillis());
        
        return assetsReceived;
    }

    /**
     * 创建留存率统计记录
     */
    private void createRetentionStatsRecord(Long userId, String username, Integer userType, 
                                          Integer codeId, String code, Long usageLogId, LocalDate usageDate) {
        try {
            // 检查是否已存在记录
            if (retentionStatsMapper.existsByUserIdAndUsageLogId(userId, usageLogId)) {
                log.warn("留存率统计记录已存在，用户ID：{}，使用记录ID：{}", userId, usageLogId);
                return;
            }
            
            UserRetentionStatsDO retentionStats = UserRetentionStatsDO.builder()
                    .userId(userId)
                    .username(username)
                    .userType(userType)
                    .codeUsageDate(usageDate)
                    .codeId(codeId)
                    .code(code)
                    .usageLogId(usageLogId)
                    .retentionStatus(0) // 0-计算中
                    .totalLoginDays(0)
                    .consecutiveLoginDays(0)
                    .build();
            
            retentionStatsMapper.insert(retentionStats);
            log.info("创建留存率统计记录成功，用户ID：{}，兑换码：{}", userId, code);
            
        } catch (Exception e) {
            log.error("创建留存率统计记录失败，用户ID：{}，兑换码：{}", userId, code, e);
        }
    }

}